<?php
/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category  Zend
 * @package   Zend_DataGrid
 * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
 * @license   http://framework.zend.com/license/new-bsd     New BSD License
 *
 */

/** @see Zend_DataGrid_Column */
require_once 'Zend/DataGrid/Column.php';

/** @see Zend_DataGrid_AssocColumns */
require_once 'Zend/DataGrid/AssocColumns.php';

/** @see Zend_Paginator */
require_once 'Zend/Paginator.php';

/** @see Zend_DataGrid_Renderer */
require_once 'Zend/DataGrid/Renderer.php';


/**
 * Class for datagrid generation
 *
 * @category  Zend
 * @package   Zend_DataGrid
 * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
 * @license   http://framework.zend.com/license/new-bsd     New BSD License
 * @version   $Id: DataGrid.php 6137 2008-09-11 21:29:27Z azoungrana $
 */
class Zend_DataGrid implements Iterator, Countable
{
    /**#@+
     * Plugin loader type constants
     */
    const RENDERER = 'RENDERER';
    const FILTER = 'FILTER';
    const COLUMN = 'COLUMN';
    /**#@-*/

    /**#@+
     * The type of renderer
     */
    const RENDER_HTML = 'Html';
    const RENDER_XML = 'Xml';
    const RENDER_CONSOLE = 'Console';
    const RENDER_CSV = 'Csv';
    const RENDER_XLS = 'Xls';
    const RENDER_TEMPLATE = 'Template';
    /**#@-*/

    /**
     * Allow sorting of column element
     *
     * @var boolean
     */
    protected $_allowSorting = false;

    /**
     * Auto generating columns from the datasource
     * @var boolean
     */
    protected $_autogenerateColumns = true;

    /**
     * Array of columns
     *
     * @var array
     */
    protected $_columns = array();

    /**
     * Array of associated columns
     *
     * @var array
     */
    protected $_columnsAssoc = array();

    /**
     * Prefix paths to use when creating columns
     *
     * @var array
     */
    protected $_columnPrefixPaths = array();

    /**
     * Prefix paths to use when creating associeted columns
     *
     * @var array
     */
    protected $_columnAssocPrefixPaths = array();

    /**
     * The data source object
     *
     * @var Zend_Paginator_Adapter_Interface
     */
    protected $_datasource = null;

    /**
     * Plugins loader list
     *
     * @var array
     */
    protected $_loaders = array();

    /**
     * The name (unique id) of datagrid
     *
     * @var string
     */
    protected $_name = null;

    /**
     * Order in which to display and iterate elements
     *
     * @var array
     */
    protected $_order = array();

    /**
     * Whether internal order has been updated or not
     *
     * @var boolean
     */
    protected $_orderUpdated = false;

    /**
     * The pager object
     *
     * @var Zend_Paginator
     */
    protected $_pager = null;

    /*
     * The pager options: currentPageNumber...
     *
     * @var array
     */
    protected $_pagerOptions = array('ItemCountPerPage' => 10,
                                     'CurrentPageNumber' => 1,
                                     'PageRange' => 10,
                                     'offset' => 1);

    /**
     * The renderer objet
     *
     * @var array
     */
    protected $_renderers = array();

    /**
     * The view object
     *
     * @var Zend_View
     */
    protected $_view = null;

    /**
     * Constructor
     *
     * @param  string|array|Zend_Config $spec
     * @param array|Zend_Config $options
     */
    public function __construct($spec = null, $options = null)
    {
        if (is_string($spec)) {
            $this->setName($spec);
        }

        if (is_array($spec)) {
            $this->setOptions($spec);
        } elseif ($spec instanceof Zend_Config) {
            $this->setConfig($spec);
        } elseif (is_array($options)) {
            $this->setOptions($options);
        } elseif ($options instanceof Zend_Config) {
            $this->setConfig($options);
        }

        //Initialize object; used by extending classes
        $this->init();

        if (null === $this->getName()) {
            require_once 'Zend/DataGrid/Exception.php';
            throw new Zend_DataGrid_Exception('Zend_DataGrid requires to assign a name to the grid');
        }
    }

    /**
     * Initialize object; used by extending classes
     *
     * @return void
     */
    public function init()
    {}


    //Options configurations


    /**
     * Sets object state from options array
     *
     * @param  array $options
     * @return Zend_DataGrid
     */
    public function setOptions(array $options)
    {
        foreach ($options as $key => $value) {
            if ($key === 'options') {
                $this->setOptions($value);
                continue;
            }

            if ($key === 'renderer') {
                foreach ($value as $rendererName => $renderOptions) {
                    $this->setRendererOptions($renderOptions, $rendererName);
                }
                continue;
            }

            $method = 'set' . ucfirst($key);

            if (method_exists($this, $method)) {
                // Setter exists; use it
                $this->$method($value);
            } else {
                // Assume it's renderer metadata
                $this->setRendererOption($key, $value);
            }
        }
        return $this;
    }

    /**
     * Set object state from Zend_Config object
     *
     * @param  Zend_Config $config
     * @return Zend_DataGrid
     */
    public function setConfig(Zend_Config $config)
    {
        return $this->setOptions($config->toArray());
    }



    //metadata


    /**
     * Set datagrid name
     *
     * @param  string $name
     * @return Zend_DataGrid
     */
    public function setName($name)
    {
        $this->_name = (string) $name;
        return $this;
    }

    /**
     * Get name attribute
     *
     * @return null|string
     */
    public function getName()
    {
        return $this->_name;
    }

    /**
     * Set element allow sorting
     *
     * @param  boolean $value
     * @return Zend_DataGrid
     */
    public function setAllowSorting($value)
    {
        $this->_allowSorting = (boolean) $value;
        return $this;
    }

    /**
     * Retrieve element allow sorting
     *
     * @return boolean
     */
    public function getAllowSorting()
    {
        return $this->_allowSorting;
    }


    //Prefix path and loaders


    /**
     * Add prefix path for plugin loader
     *
     * If no $type specified, assumes it is a base path for filters and
     * sets each according to the following rules:
     * - filters: $prefix = $prefix . '_Filter'
     * - renderers: $prefix = $prefix . '_Renderer'
     * - columnss: $prefix = $prefix . '_Column'
     *
     * Otherwise, the path prefix is set on the appropriate plugin loader.
     *
     * @param string $prefix
     * @param  string $path
     * @param string $type
     * @return Zend_DataGrid
     * @throws Zend_DataGrid_Exception for invalid type
     */
    public function addPrefixPath($prefix, $path, $type = null)
    {
        $type = strtoupper($type);
        switch ($type) {
            case self::RENDERER:
            case self::FILTER:
            case null:
                $prefix = rtrim($prefix, '_');
                $path   = rtrim($path, DIRECTORY_SEPARATOR);
                foreach (array( self::FILTER) as $type) {
                    $cType        = ucfirst(strtolower($type));
                    $pluginPath   = $path . DIRECTORY_SEPARATOR . $cType . DIRECTORY_SEPARATOR;
                    $pluginPrefix = $prefix . '_' . $cType;
                    $loader       = $this->getPluginLoader($type);
                    $loader->addPrefixPath($pluginPrefix, $pluginPath);
                }
                return $this;

            default:
                require_once 'Zend/DataGrid/Exception.php';
                throw new Zend_DataGrid_Exception(sprintf('Invalid type "%s" provided to getPluginLoader()', $type));
        }
    }

    /**
     * Add many prefix paths at once
     *
     * @param  array $spec
     * @return Zend_DataGrid
     */
    public function addPrefixPaths(array $spec)
    {
        if (isset($spec['prefix']) && isset($spec['path'])) {
            return $this->addPrefixPath($spec['prefix'], $spec['path']);
        }
        foreach ($spec as $type => $paths) {
            if (is_numeric($type) && is_array($paths)) {
                $type = null;
                if (isset($paths['prefix']) && isset($paths['path'])) {
                    if (isset($paths['type'])) {
                        $type = $paths['type'];
                    }
                    $this->addPrefixPath($paths['prefix'], $paths['path'], $type);
                }
            } elseif (!is_numeric($type)) {
                if (!isset($paths['prefix']) || !isset($paths['path'])) {
                    continue;
                }
                $this->addPrefixPath($paths['prefix'], $paths['path'], $type);
            }
        }
        return $this;
    }


    // Loaders


    /**
     * Set plugin loader to use for validator or filter chain
     *
     * @param  Zend_Loader_PluginLoader_Interface $loader
     * @param  string $type  'column', 'filter', 'renderer'
     * @return Zend_DataGrid
     * @throws Zend_DataGrid_Exception on invalid type
     */
    public function setPluginLoader(Zend_Loader_PluginLoader_Interface $loader, $type)
    {
        $type = strtoupper($type);
        switch ($type) {
            case self::COLUMN:
            case self::RENDERER:
            case self::FILTER:
                $this->_loaders[$type] = $loader;
                return $this;
            default:
                require_once 'Zend/DataGrid/Exception.php';
                throw new Zend_DataGrid_Exception(sprintf('Invalid type "%s" provided to setPluginLoader()', $type));
        }
    }

    /**
     * Retrieve plugin loader for renderer/filter chain
     *
     * Instantiates with default rules if none available for that type. Use
     *  'filter' or 'renderer' or 'column' for $type.
     *
     * @param  string $type
     * @return Zend_Loader_PluginLoader
     * @throws Zend_Loader_Exception on invalid type.
     */
    public function getPluginLoader($type)
    {
        $type = strtoupper($type);
        switch ($type) {
            case self::COLUMN:
                $prefixSegment = 'DataGrid_Column';
                $pathSegment   = 'DataGrid/Column';
                if (!isset($this->_loaders[$type])) {
                    require_once 'Zend/Loader/PluginLoader.php';
                    $this->_loaders[$type] = new Zend_Loader_PluginLoader(
                            array('Zend_' . $prefixSegment . '_' => 'Zend/' . $pathSegment . '/')
                    );
                }
                return $this->_loaders[$type];
            case self::RENDERER:
                $prefixSegment = 'DataGrid_Renderer';
                $pathSegment   = 'DataGrid/Renderer';
                if (!isset($this->_loaders[$type])) {
                    require_once 'Zend/Loader/PluginLoader.php';
                    $this->_loaders[$type] = new Zend_Loader_PluginLoader(
                            array('Zend_' . $prefixSegment . '_' => 'Zend/' . $pathSegment . '/')
                    );
                }
                return $this->_loaders[$type];
            case self::FILTER:
                $prefixSegment = ucfirst(strtolower($type));
                $pathSegment   = $prefixSegment;
                if (!isset($prefixSegment)) {
                    $prefixSegment = 'Filter';
                    $pathSegment   = 'Filter';
                }
                if (!isset($this->_loaders[$type])) {
                    require_once 'Zend/Loader/PluginLoader.php';
                    $this->_loaders[$type] = new Zend_Loader_PluginLoader(
                            array('Zend_' . $prefixSegment . '_' => 'Zend/' . $pathSegment . '/')
                    );
                }
                return $this->_loaders[$type];
            default:
                require_once 'Zend/DataGrid/Exception.php';
                throw new Zend_DataGrid_Exception(sprintf('Invalid type "%s" provided to getPluginLoader()', $type));
        }
    }


    //columns

    /**
     * Create an oolumn
     *
     * Acts as a factory for creating columns.
     *
     * @param  string $type
     * @param  string $name
     * @param  array|Zend_Config $options
     * @return Zend_DataGrid_Column
     */
    public function createColumn($type, $name, $options = null)
    {
        if (!is_string($type)) {
            require_once 'Zend/DataGrid/Exception.php';
            throw new Zend_DataGrid_Exception('Column type must be a string indicating type');
        }

        if (!is_string($name)) {
            require_once 'Zend/DataGrid/Exception.php';
            throw new Zend_DataGrid_Exception('Column name must be a string');
        }

        $prefixPaths              = array();
        if (!empty($this->_columnPrefixPaths)) {
            $prefixPaths = $this->_columnPrefixPaths;
        }

        if ($options instanceof Zend_Config) {
            $options = $options->toArray();
        }

        if ((null === $options) || !is_array($options)) {
            $options = array('prefixPath' => $prefixPaths);
        } elseif (is_array($options)) {
            if (array_key_exists('prefixPath', $options)) {
                $options['prefixPath'] = array_merge($prefixPaths, $options['prefixPath']);
            } else {
                $options['prefixPath'] = $prefixPaths;
            }
        }

        $class = $this->getPluginLoader(self::COLUMN)->load($type);
        $column = new $class($name, $options);

        return $column;
    }

    /**
     * Add or Inserts a column at the specified index in the columns array
     *
     * @param  string|Zend_DataGrid_Column $element
     * @param  string $name
     * @param  array|Zend_Config $options
     * @return Zend_DataGrid
     */
    public function addColumn($column, $name = null, $options = null)
    {
        if (is_string($column)) {
            if (null === $name) {
                require_once 'Zend/DataGrid/Exception.php';
                throw new Zend_DataGrid_Exception('Columns specified by string must have an accompanying name');
            }

            $this->_columns[$name] = $this->createColumn($column, $name, $options);
        } elseif ($column instanceof Zend_DataGrid_Column) {
            $prefixPaths              = array();
            if (!empty($this->_columnPrefixPaths)) {
                $prefixPaths = array_merge($prefixPaths, $this->_columnPrefixPaths);
            }

            if (null === $name) {
                $name = $column->getName();
            }

            $this->_columns[$name] = $column;
            $this->_columns[$name]->addPrefixPaths($prefixPaths);
        }

        $this->_order[$name] = $this->_columns[$name]->getOrder();
        $this->_orderUpdated = true;

        return $this;
    }

    /**
     * Add multiple columns at once
     *
     * @param array $columns
     * @return Zend_DataGrid
     */
    public function addColumns(array $columns)
    {
        foreach ($columns as $key => $spec) {
            $name = null;
            if (!is_numeric($key)) {
                $name = $key;
            }

            if (is_string($spec) || ($spec instanceof Zend_DataGrid_Column)) {
                $this->addColumn($spec, $name);
                continue;
            }

            if (is_array($spec)) {
                $argc = count($spec);
                $options = array();
                if (isset($spec['type'])) {
                    $type = $spec['type'];
                    if (isset($spec['name'])) {
                        $name = $spec['name'];
                    }
                    if (isset($spec['options'])) {
                        $options = $spec['options'];
                    }
                    $this->addColumn($type, $name, $options);
                } else {
                    switch ($argc) {
                        case 0:
                            continue;
                        case (1 <= $argc):
                            $type = array_shift($spec);
                        case (2 <= $argc):
                            if (null === $name) {
                                $name = array_shift($spec);
                            } else {
                                $options = array_shift($spec);
                            }
                        case (3 <= $argc):
                            if (empty($options)) {
                                $options = array_shift($spec);
                            }
                        default:
                            $this->addColumn($type, $name, $options);
                    }
                }
            }
        }
        return $this;
    }

    /**
     * Set datagrid columnss (overwrites existing columns)
     *
     * @param array $columns
     * @return Zend_DataGrid
     */
    public function setColumns(array $columns)
    {
        $this->clearColumns();
        return $this->addColumns($columns);
    }

    /**
     * Return a column by its name, search also in the grouping columns
     *
     * @param string $name
     * @return null|Zend_DataGrid_Column
     */
    public function getColumn($name)
    {
        if (array_key_exists($name, $this->_columns)) {
            return $this->_columns[$name];
        }
        return null;
    }

    /**
     * Return the current columns
     *
     * @return array
     */
    public function getColumns()
    {
        return $this->_columns;
    }

    /**
     * Remove column specify by it name
     *
     * @param string
     * @return boolean
     */
    public function removeColumn($name)
    {
        $name = (string) $name;
        if (isset($this->_columns[$name])) {
            unset($this->_columns[$name]);
            if (array_key_exists($name, $this->_order)) {
                unset($this->_order[$name]);
                $this->_orderUpdated = true;
            } else {
                foreach ($this->_columnsAssoc as $group) {
                    if (null !== $group->getColumn($name)) {
                        $group->removeColumn($name);
                    }
                }
            }
            return true;
        }

        return false;
    }

    /**
     * Removes columns specify by its names ou removes all columns
     *
     * @param array
     * @return Zend_DataGrid
     */
    public function removeColumns(array $names = null)
    {
        foreach ($names as $name)
            $this->removeColumn($name);

        return $this;
    }

    /**
     * Remove all form columns
     *
     * @return Zend_DataGrid
     */
    public function clearColumns()
    {
        foreach (array_keys($this->_columns) as $key) {
            if (array_key_exists($key, $this->_order)) {
                unset($this->_order[$key]);
            }
        }
        $this->_columns     = array();
        $this->_orderUpdated = true;
        return $this;
    }

    /**
     * Auto generate the columns if no explicity specify
     *
     * @return Zend_DataGrid
     */
    public function setAutogenerateColumns($value)
    {
        $this->_autogenerateColumns = (boolean) $value;

        return $this;
    }

    /**
     * Get auto generate the columns
     *
     * @return boolean
     */
    public function getAutogenerateColumns()
    {
        return $this->_autogenerateColumns;
    }


    //associated columns


    /**
     * Add a display group
     *
     * Groups named columns for display purposes.
     *
     * @param  array $columns
     * @param  string $name
     * @param  array|Zend_Config $options
     * @return Zend_DataGrid
     */
    public function addAssocColumn(array $columns, $name, $options = null)
    {
        $group = array();
        foreach ($columns as $column) {
            if (isset($this->_columns[$column])) {
                $add = $this->getColumn($column);
                if (null !== $add) {
                    unset($this->_order[$column]);
                    $group[] = $add;
                }
            }
        }
        if (empty($group)) {
            require_once 'Zend/DataGrid/Exception.php';
            throw new Zend_DataGrid_Exception('No valid columns specified for display group');
        }

        $name = (string) $name;

        if (is_array($options)) {
            $options['columns'] = $group;
        } elseif ($options instanceof Zend_Config) {
            $options = $options->toArray();
            $options['columns'] = $group;
        } else {
            $options = array('columns' => $group);
        }

        if (isset($options['assocColumnClass'])) {
            $class = $options['assocColumnClass'];
            unset($options['assocColumnClass']);
        } else {
            $class = $this->getDefaultAssocColumnClass();
        }

        if (!class_exists($class)) {
            require_once 'Zend/Loader.php';
            Zend_Loader::loadClass($class);
        }
        $this->_columnsAssoc[$name] = new $class(
            $name,
            $options
        );

        if (!empty($this->_columnAssocPrefixPaths)) {
            $this->_columnsAssoc[$name]->addPrefixPaths($this->_columnAssocPrefixPaths);
        }

        $this->_order[$name] = $this->_columnsAssoc[$name]->getOrder();
        $this->_orderUpdated = true;

        return $this;
    }

    /**
     * Add a display group object (used with cloning)
     *
     * @param  Zend_Form_DisplayGroup $group
     * @param  string|null $name
     * @return Zend_Form
     */
    protected function _addAssocColumnObject(Zend_DataGrid_AssocColumns $group, $name = null)
    {
        if (null === $name) {
            $name = $group->getName();
            if (empty($name)) {
                require_once 'Zend/DataGrid/Exception.php';
                throw new Zend_DataGrid_Exception('Invalid display group added; requires name');
            }
        }

        $this->_columnsAssoc[$name] = $group;

        if (!empty($this->_columnAssocPrefixPaths)) {
            $this->_columnsAssoc[$name]->addPrefixPaths($this->_columnAssocPrefixPaths);
        }

        $this->_order[$name] = $this->_columnsAssoc[$name]->getOrder();
        $this->_orderUpdated = true;
        return $this;
    }

    /**
     * Add multiple display groups at once
     *
     * @param  array $groups
     * @return Zend_DataGrid
     */
    public function addAssocColumns(array $groups)
    {
        foreach ($groups as $key => $spec) {
            $name = null;
            if (!is_numeric($key)) {
                $name = $key;
            }

            if ($spec instanceof Zend_DataGrid_AssocColumns) {
                $this->_addAssocColumnObject($spec);
            }

            if (!is_array($spec) || empty($spec)) {
                continue;
            }

            $argc    = count($spec);
            $options = array();

            if (isset($spec['columns'])) {
                $elements = $spec['columns'];
                if (isset($spec['name'])) {
                    $name = $spec['name'];
                }
                if (isset($spec['options'])) {
                    $options = $spec['options'];
                }
                $this->addAssocColumn($elements, $name, $options);
            } else {
                switch ($argc) {
                    case (1 <= $argc):
                        $elements = array_shift($spec);
                        if (!is_array($elements) && (null !== $name)) {
                            $elements = array_merge((array) $elements, $spec);
                            $this->addAssocColumn($elements, $name);
                            break;
                        }
                    case (2 <= $argc):
                        if (null !== $name) {
                            $options = array_shift($spec);
                            $this->addAssocColumn($elements, $name, $options);
                            break;
                        }
                        $name = array_shift($spec);
                    case (3 <= $argc):
                        $options = array_shift($spec);
                    default:
                        $this->addAssocColumn($elements, $name, $options);
                }
            }
        }
        return $this;
    }

    /**
     * Add multiple display groups (overwrites)
     *
     * @param  array $groups
     * @return Zend_DataGrid
     */
    public function setAssocColumns(array $groups)
    {
        return $this->clearAssocColumns()
                    ->addAssocColumns($groups);
    }

    /**
     * Return a display group
     *
     * @param  string $name
     * @return array|null
     */
    public function getAssocColumn($name)
    {
        $name = (string) $name;
        if (isset($this->_columnsAssoc[$name])) {
            return $this->_columnsAssoc[$name];
        }

        return null;
    }

    /**
     * Return all display groups
     *
     * @return array
     */
    public function getAssocColumns()
    {
        return $this->_columnsAssoc;
    }

    /**
     * Remove a display group by name
     *
     * @param  string $name
     * @return boolean
     */
    public function removeAssocColumn($name)
    {
        $name = (string) $name;
        if (array_key_exists($name, $this->_columnsAssoc)) {
            foreach ($this->_columnsAssoc[$name] as $key => $element) {
                if (array_key_exists($key, $this->_elements)) {
                    $this->_order[$key]  = $element->getOrder();
                    $this->_orderUpdated = true;
                }
            }
            unset($this->_columnsAssoc[$name]);

            if (array_key_exists($name, $this->_order)) {
                unset($this->_order[$name]);
                $this->_orderUpdated = true;
            }
            return true;
        }

        return false;
    }

    /**
     * Remove all display groups
     *
     * @return Zend_DataGrid
     */
    public function clearAssocColumns()
    {
        foreach ($this->_columnsAssoc as $key => $group) {
            if (array_key_exists($key, $this->_order)) {
                unset($this->_order[$key]);
            }
            foreach ($group as $name => $element) {
                if (isset($this->_elements[$name])) {
                    $this->_order[$name] = $element->getOrder();
                }
                $this->_order[$name] = $element->getOrder();
            }
        }
        $this->_columnsAssoc = array();
        $this->_orderUpdated  = true;

        return $this;
    }

    /**
     * Get the order of columns and associated columns in datagrid
     *
     * @return Zend_DataGrid
     */
    protected function _sort()
    {
        if ($this->_orderUpdated) {
            // get the last index in the order array
            $lastNumber = 0;
            foreach ($this->_order as $o) {
                if ($o > $lastNumber) $lastNumber = $o;
            }
            //fix and index when no oorder set
            foreach ($this->_order as $col => $o) {
                if (null == $o)  $this->_order[$col] = ++$lastNumber;
            }

            asort($this->_order, SORT_NUMERIC);
            $this->_orderUpdated = false;
        }

        return $this;
    }


    //datasource


    /**
     * Get the currently loaded DataSource object
     *
     * @return Zend_Paginator_Adapter_Interface
     */
    public function getDatasource()
    {
        return $this->_pager->getAdapter();
    }

    /**
     * Set the datasource of the datagrid component
     *
     * @param mixed $datasource the datasource
     * @param Zend_Config $config The options for datasource, such as the type of datasource, xml, dsn...
     * @return Zend_DataGrid
     */
    public function setDatasource($datasource, $config = null)
    {
        require_once 'Zend/Paginator.php';

        $pager = Zend_Paginator::factory($datasource);

        if ($config instanceof Zend_Config) {
            $datasource->setConfig($config);
        }

        $this->_pager = $pager;

        return $this;
    }

    /**
     * Bind data to datagrid
     *
     * Proxies to {@link setDatasource()}.
     * @return Zend_DataGrid
     */
    public function dataBind($data)
    {
        $this->setDatasource($data);

        return $this;
    }

    /**
     * Set a single datasource option
     *
     * @param string $name
     * @param mixed $value
     * @return Zend_DataGrid
     */
    public function setDataSourceOption($name, $value)
    {
        $adapter = $this->_pager->getAdapter();

        $function = 'set' . ucfirst($name);
        $adapter->$function($value);

        return $this;
    }

    /**
     * Set multiple datasource options
     *
     * @param array $options An associative array of the form: array("option_name" => "option_value",...)
     * @return Zend_DataGrid
     */
    public function setDataSourceOptions(array $options)
    {
        foreach ($options as $name => $value) {
            $this->setDataSourceOption($name, $value);
        }

        return $this;
    }

    /**
     * Returns an array of items for a page.
     *
     * @param  integer $offset Page offset
     * @param  integer $itemCountPerPage Number of items per page
     * @return array
     */
    public function getItems($offset = null, $itemCountPerPage = null)
    {
        //get the offset and itemCountPerPage in the session
        if (null === $offset) {
            //get it in the session
            $offset = 1;
        }

        if (null === $itemCountPerPage) {
            //get it in the session
            $itemCountPerPage = 3;
        }

        return $this->_pager->getAdapter()->getItems($offset, $itemCountPerPage);
    }

    /**
     * Returns the total number of records
     *
     * @return integer
     */
    public function getItemsCount()
    {
        return $this->_pager->getAdapter()->count();
    }

    /**
     * Request the recordset to sort its data
     *
     * @return Zend_DataGrid
     */
    public function sortItems()
    {
        $items = $this->getItems();
    }


    //pager


    /**
     * Retrieves the datagrid paginator
     *
     * @return Zend_Paginator
     */
    public function getPager()
    {
        if (null === $this->_pager) {
            require_once 'Zend/Paginator.php';
            $this->_pager = Zend_Paginator::factory(0);

            $this->_pager->setItemCountPerPage($this->_pagerOptions['ItemCountPerPage']);

            $this->_pager->setCurrentPageNumber($this->_pagerOptions['CurrentPageNumber']);

            $this->_pager->setPageRange($this->_pagerOptions['PageRange']);
        }

        return $this->_pager;
    }

    /**
     * Sets the number of items per page.
     *
     * @param  integer $itemCountPerPage
     * @return Zend_DataGrid $this
     */
    public function setItemCountPerPage($itemCountPerPage)
    {
        $this->_pagerOptions['ItemCountPerPage'] = $itemCountPerPage;

        return $this;
    }

    /**
     * Returns the number of items per page.
     *
     * @return integer
     */
    public function getItemCountPerPage()
    {
        return $this->_pagerOptions['ItemCountPerPage'];
    }

    /**
     * Sets the current page number.
     *
     * @param  integer $pageNumber Page number
     * @return Zend_DataGrid
     */
    public function setCurrentPageNumber($pageNumber)
    {
        $this->_pagerOptions['CurrentPageNumber'] = $pageNumber;
        return $this;
    }

    /**
     * Returns the current page number.
     *
     * @return integer
     */
    public function getCurrentPageNumber()
    {
        return $this->_pagerOptions['CurrentPageNumber'];
    }

    /**
     * Sets the page range (see property declaration above).
     *
     * @param  integer $pageRange
     * @return Zend_DataGrid $this
     */
    public function setPageRange($pageRange)
    {
        $this->_pagerOptions['PageRange'] = $pageRange;

        return $this;
    }

    /**
     * Returns the page range (see property declaration above).
     *
     * @return integer
     */
    public function getPageRange()
    {
        return $this->_pagerOptions['PageRange'];
    }


    //rendering and its options


    /**
     * Set view object
     *
     * @param  Zend_View_Interface $view
     * @return Zend_DataGrid
     */
    public function setView(Zend_View_Interface $view = null)
    {
        $this->_view = $view;
        return $this;
    }

    /**
     * Retrieve view object
     *
     * If none registered, attempts to pull from ViewRenderer.
     *
     * @return Zend_View_Interface|null
     */
    public function getView()
    {
        if (null === $this->_view) {
            require_once 'Zend/Controller/Action/HelperBroker.php';
            $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
            $this->setView($viewRenderer->view);
        }

        return $this->_view;
    }

    /**
     * Set Renderer
     *
     * @param string|Zend_DataGrid_Renderer $type
     * @param array|Zend_Config $options
     * @return Zend_DataGrid
     * @throws Zend_DataGrid_Renderer_Exception when the renderer not a class of Zend_DataGrid_Renderer
     */
    public function setRenderer($type, $options = null)
    {
        if (is_string($type)) {
            $renderer = Zend_DataGrid_Renderer::factory($type, $this);
        } else if ($type instanceof Zend_DataGrid_Renderer) {
            $renderer = $type;
            $type = $renderer->getType();
            $renderer->setGridElement($this);
        }

        if (!($renderer instanceof Zend_DataGrid_Renderer))
            throw new Zend_DataGrid_Renderer_Exception('Must be an instance of Zend_DataGrid_Renderer_Abstract');

        if (null !== $options) {
            $renderer->setOptions($options);
        }

        $this->_renderers[$type] = $renderer;

        return $this;
    }

    /**
     * Get the current or default Rendering object
     * The renderer will be create if not exist
     *
     * @param string $type
     * @return Zend_DataGrid_Renderer
     */
    public function getRenderer($type = null)
    {
        if (null === $type) {
            $type = Zend_DataGrid_Renderer::getDefaultType();
        }

        $type = ucfirst($type);

        if (! array_key_exists($type, $this->_renderers)) {
            $renderer = Zend_DataGrid_Renderer::factory($type, $this);
            $this->setRenderer($renderer);
        }

        return $this->_renderers[$type];
    }

    /**
     * Set the default renderer type
     *
     * @param string $type
     * @return Zend_DataGrid
     */
    public function setRendererDefaultType($type)
    {
        Zend_DataGrid_Renderer::setDefaultType($type);

        return $this;
    }

    /**
     * Get the default renderer type
     *
     * @return string
     */
    public function getRendererDefaultType()
    {
        return Zend_DataGrid_Renderer::getDefaultType();
    }

    /**
     * Set a grid renderer option
     *
     * @param string $name
     * @param mixed $value
     * @param string $rendererType
     * @return Zend_DataGrid
     */
    public function setRendererOption($name, $value, $rendererType = null)
    {
        $renderer = $this->getRenderer($rendererType);

        $method = 'set' . ucfirst($name);

        if (method_exists($renderer, $method))
            $renderer->$method($value);
        else
            $renderer->setOption($name, $value);

        return $this;
    }

    /**
     * Get the grid renderer option
     *
     * @param string $name
     * @param string $rendererType
     * @return mixed
     */
    public function getRendererOption($name, $rendererType = null)
    {
        $renderer = $this->getRenderer($rendererType);

        $method = 'get' . ucfirst($name);

        if (method_exists($renderer, $method)) {
            return $renderer->$method();
        } else {
            return $renderer->getOption($name);
        }
    }

    /**
     * Set the grid renderer options
     *
     * @param array $options An associative array of the form: array("option_name" => "option_value",...)
     * @param string $rendererType the name of renderer to assign options
     * @return Zend_DataGrid
     */
    public function setRendererOptions(array $options, $rendererType = null)
    {
        $this->getRenderer($rendererType)->setOptions($options);

        return $this;
    }

    /**
     * Get the grid renderer options
     *
     * @param string $rendererType
     * @return array
     */
    public function getRendererOptions($rendererType = null)
    {
        return $this->getRenderer($rendererType)->getOptions();
    }

    /**
     * Render the datagrid
     *
     * @param Zend_View_Interface $view
     * @param string $rendererType
     * @return string
     */
    public function render(Zend_View_Interface $view = null, $rendererType = null)
    {
        if (null !== $view) {
            $this->setView($view);
        }

        if ($this->_autogenerateColumns) {
            $columns = array();
            $items = $this->getItems();
            foreach ($items as $item) {
                foreach ($item as $label=>$value) {
                    $column = $this->createColumn('Literal', $label);
                    $column->setHeaderText($label);
                    $column->setDataField($label);
                    $column->setFooterText($label);
                    $this->addColumn($column);
                }
                break;
            }
        }

        $renderer = $this->getRenderer($rendererType);
        $renderer->setView($this->getView());

        return $renderer->render();
    }

    /**
     * Render the datagrid pager
     *
     * @param Zend_View_Interface $view
     * @param string $rendererType
     * @return string
     */
    public function renderPager(Zend_View_Interface $view = null, $rendererType = null)
    {
        $renderer = $this->getRenderer($rendererType);

        if (null !== $view) {
            $this->setView($view);
        }

        $renderer->setView($this->getView());

        return $renderer->renderPager();
    }


    //PHP Magic


    /**
     * Overloading: access to columns, assoc columns, and renderers
     *
     * @param  string $name
     * @return Zend_DataGrid_Column|Zend_DataGrid_AssocColumn|Zend_DataGrid_Renderer|null
     */
    public function __get($name)
    {
        $name = (string) $name;

        if (isset($this->_columns[$name])) {
            return $this->getColumn($name);
        } elseif (isset($this->_columnsAssoc[$name])) {
            return $this->getAssocColumn($name);
        } elseif (isset($this->_renderers[$name])) {
            return $this->getRenderer($name);
        }

        return null;
    }

    /**
     * Overloading: access to columns, assoc columns, and renderers
     *
     * @param  string $name
     * @param  Zend_DataGrid_Column|Zend_DataGrid_AssocColumn|Zend_DataGrid_Renderer $value
     * @return void
     * @throws Zend_DataGrid_Exception for invalid $value
     */
    public function __set($name, $value)
    {
        if ($value instanceof Zend_DataGrid_Column) {
            $this->addColumn($value, $name);
            return $this;
        } elseif ($value instanceof Zend_DataGrid_AssocColumn) {
            $this->_addAssocColumnObject($value, $name);
            return $this;
        } elseif ($value instanceof Zend_DataGrid_Renderer) {
            $this->setRenderer($value);
            return $this;
        } elseif (is_array($value)) {
            $this->addAssocColumn($value, $name);
            return $this;
        }

        require_once 'Zend/DataGrid/Exception.php';
        if (is_object($value)) {
            $type = get_class($value);
        } else {
            $type = gettype($value);
        }
        throw new Zend_DataGrid_Exception('Only datagrid colums and renderers  may be overloaded; variable of type "' . $type . '" provided');
    }

    /**
     * Overloading: access to columns, assoc columns, and renderers
     *
     * @param  string $name
     * @return boolean
     */
    public function __isset($name)
    {
        if (isset($this->_columns[$name])
            || isset($this->_columnsAssoc[$name])
            || isset($this->_renderers[$name]))
        {
            return true;
        }

        return false;
    }

    /**
     * Overloading: access to columns, assoc columns, and renderers
     *
     * @param  string $name
     * @return void
     */
    public function __unset($name)
    {
        if (isset($this->_columns[$name])) {
            $this->removeColumn($name);
        } elseif (isset($this->_columnsAssoc[$name])) {
            $this->removeAssocColumn($name);
        } elseif (isset($this->_renderers[$name])) {
            unset($this->_renderers[$name]);
        }
    }

    /**
     * Overloading: allow get/set specific renderers options
     *
     * Call the renderer to personnalize it.
     *
     * @param  string $method
     * @param  array $args
     * @return string
     * @throws Zend_DataGrid_Exception for invalid renderer or invalid method call
     */
    public function __call($method, $args)
    {
        if (!method_exists($this->getRenderer(), $method)) {
            require_once 'Zend/DataGrid/Exception.php';
            throw new Zend_DataGrid_Exception(sprintf('Method %s does not exist', $method));
        }

        $seed = $value = null;
        if (0 < count($args)) {
            $seed = array_shift($args);
            $value = array_shift($args);
        }

        if (null === $seed) {
            return $this->getRenderer()->$method();
        } else {
            $this->getRenderer()->$method($seed, $value);
            return $this;
        }
    }


    // Interfaces: Iterator, Countable


    /**
     * Current column/assoc column
     *
     * @return Zend_DataGrid_Column|Zend_DataGrid_AssocColumns
     */
    public function current()
    {
        $this->_sort();
        current($this->_order);
        $key = key($this->_order);

        if (isset($this->_columns[$key])) {
            return $this->getColumn($key);
        } elseif (isset($this->_columnsAssoc[$key])) {
            return $this->getAssocColumn($key);
        } else {
            require_once 'Zend/DataGrid/Exception.php';
            throw new Zend_DataGrid_Exception(sprintf('Corruption detected in datagrid; invalid key ("%s") found in internal iterator', (string) $key));
        }
    }

    /**
     * Current column/assoc column
     *
     * @return string
     */
    public function key()
    {
        $this->_sort();
        return key($this->_order);
    }

    /**
     * Move pointer to next column/assoc column
     *
     * @return void
     */
    public function next()
    {
        $this->_sort();
        next($this->_order);
    }

    /**
     * Move pointer to beginning of column/assoc column loop
     *
     * @return void
     */
    public function rewind()
    {
        $this->_sort();
        reset($this->_order);
    }

    /**
     * Determine if current column/assoc column is valid
     *
     * @return bool
     */
    public function valid()
    {
        $this->_sort();
        return (current($this->_order) !== false);
    }

    /**
     * Count of column/assoc column that are iterable
     *
     * @return int
     */
    public function count()
    {
        return count($this->_order);
    }


    // events


    public function getEditItem() {}

    public function setEditItemIndex($value) {}

    public function getEditItemIndex() {}

    public function getSelectedItem() {}

    public function setSelectedItemIndex() {}

    public function getSelectedItemIndex() {}


    public function onEditCommand($param) {}

    public function onCancelCommand($param) {}

    public function onSelectCommand($param) {}

    public function onDeleteCommand($param) {}

    public function onUpdateCommand($param) {}

    public function onPageIndexChanged($param) {}

    public function onSortCommand($param) {}

}
